import requests
from typing import Any, Dict, Optional, Type, TypeVar
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from pydantic import BaseModel
from response_handler import ResponseHandler, ResponseData
from exceptions import RequestError, ResponseError

T = TypeVar("T", bound=BaseModel)

class HttpClient:
    def __init__(
        self,
        base_url: str = "",
        timeout: int = 30,
        max_retries: int = 3,
        token: Optional[str] = None,
        headers: Optional[Dict[str, str]] = None
    ):
        self.base_url = base_url.rstrip('/')
        self.timeout = timeout
        self.session = requests.Session()
        
        # 设置重试策略
        retry_strategy = Retry(
            total=max_retries,
            backoff_factor=0.5,
            status_forcelist=[500, 502, 503, 504]
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        self.session.mount("http://", adapter)
        self.session.mount("https://", adapter)
        
        # 设置默认请求头
        self.session.headers.update({
            'Content-Type': 'application/json',
            'Accept': 'application/json'
        })
        
        if token:
            self.set_token(token)
            
        if headers:
            self.session.headers.update(headers)
            
    def set_token(self, token: str) -> None:
        """设置认证token"""
        self.session.headers.update({'Authorization': f'Bearer {token}'})
        
    def _make_url(self, endpoint: str) -> str:
        """构造完整的URL"""
        if endpoint.startswith(('http://', 'https://')):
            return endpoint
        return f"{self.base_url}/{endpoint.lstrip('/')}"
        
    def _handle_response(self, response: requests.Response, data_model: Optional[Type[T]] = None) -> ResponseData:
        """处理响应"""
        try:
            response.raise_for_status()
            response_json = response.json()
        except requests.exceptions.JSONDecodeError:
            raise ResponseError("响应解析失败")
        except requests.exceptions.HTTPError as e:
            raise RequestError(f"HTTP错误: {str(e)}")
            
        response_data = ResponseHandler.parse_response(response_json, data_model)
        if not ResponseHandler.is_success(response_data):
            raise ResponseError(ResponseHandler.get_error_msg(response_data))
            
        return response_data
        
    def request(
        self,
        method: str,
        endpoint: str,
        data_model: Optional[Type[T]] = None,
        params: Optional[Dict] = None,
        data: Optional[Dict] = None,
        json: Optional[Dict] = None,
        **kwargs
    ) -> ResponseData:
        """发送请求"""
        url = self._make_url(endpoint)
        try:
            response = self.session.request(
                method=method,
                url=url,
                params=params,
                data=data,
                json=json,
                timeout=self.timeout,
                **kwargs
            )
            return self._handle_response(response, data_model)
        except requests.exceptions.Timeout:
            raise RequestError("请求超时")
        except requests.exceptions.RequestException as e:
            raise RequestError(f"请求异常: {str(e)}")
            
    def get(self, endpoint: str, data_model: Optional[Type[T]] = None, params: Optional[Dict] = None, **kwargs) -> ResponseData:
        return self.request('GET', endpoint, data_model=data_model, params=params, **kwargs)
        
    def post(self, endpoint: str, data_model: Optional[Type[T]] = None, json: Optional[Dict] = None, data: Optional[Dict] = None, **kwargs) -> ResponseData:
        return self.request('POST', endpoint, data_model=data_model, json=json, data=data, **kwargs)
        
    def put(self, endpoint: str, data_model: Optional[Type[T]] = None, json: Optional[Dict] = None, data: Optional[Dict] = None, **kwargs) -> ResponseData:
        return self.request('PUT', endpoint, data_model=data_model, json=json, data=data, **kwargs)
        
    def delete(self, endpoint: str, data_model: Optional[Type[T]] = None, **kwargs) -> ResponseData:
        return self.request('DELETE', endpoint, data_model=data_model, **kwargs) 